package actions.ventas;

import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import services.historico.VentaHistoricoService;
import services.movimiento.MovimientoCajaService;
import services.producto.ProductoService;
import services.venta.CriterioDeCobroService;
import services.venta.ItemFacturaService;
import services.venta.VentaService;
import util.GSonHelper;
import actions.base.BaseAction;
import bo.Entrega;
import bo.PersistentObject;
import bo.Venta;

import com.google.gson.Gson;

import dto.EntregaDTO;
import dto.FormaDeCobroDTO;
import dto.ItemFacturaDTO;
import dto.MovimientoCajaDTO;
import dto.ProductoDTO;
import dto.SearchDTO;
import dto.VentaDTO;
import exceptions.CannotDeleteException;
import exceptions.CannotSaveException;
import exceptions.EntityNotFoundException;

public class VentaAction extends BaseAction {
	
	private static final long serialVersionUID = 1L;

	private VentaService ventaService;
	
	private MovimientoCajaService movimientoCajaService;
	
	private ItemFacturaService itemFacturaService;
	
	private ProductoService productoService;
	
	private VentaHistoricoService ventaHistoricoService;
	
	private CriterioDeCobroService criterioDeCobroService;
	
	private VentaDTO ventaDTO;
	
	private String items;
	
	private Date fechaInicio;
	
	private Date fechaFin;

	private List<FormaDeCobroDTO> formasDeCobro;
	
	@Override
	public String execute() {
		return SUCCESS;
	}
	
	/**
	 * Agrega un nuevo venta
	 * @return
	 * @throws EntityNotFoundException 
	 */
	public String agregar() {
		
		// TODO hacer validaciones
		
		// No se han agregado items a la factura
		if (items == null) {
			setOk("false");
			setMensaje("No se ha podido almacenar la venta. No hay items asociados a la factura");
			return SUCCESS_AGREGAR;
		}
		
		// Si es una EDICION, almaceno la venta vieja para posterior uso
		VentaDTO ventaVieja = null;
		if (ventaDTO.getId() != null && !"".equals(ventaDTO.getId())) {
			try {
				ventaVieja = ventaService.findById(ventaDTO.getId());
			} catch (EntityNotFoundException e) {
				setMensaje("No se ha podido almacenar la venta. La venta es inexistente.");
				return SUCCESS_AGREGAR;
			}
			ventaService.getVentaDAO().getHibernateTemplate().clear();
			ventaService.getVentaDAO().getHibernateTemplate().flush();
			ventaService.getVentaDAO().getHibernateTemplate().evict(ventaVieja);
		}
		
		// Una venta con un retiro PACTADO, tiene una entrega con estado PENDIENTE
		String retiro = ventaDTO.getRetiro();
		EntregaDTO entregaDTO = ventaDTO.getEntregaDTO();
		if (entregaDTO != null && Venta.RETIRO_PACTADO.equalsIgnoreCase(retiro)) {
			entregaDTO.setEstado(Entrega.ESTADO_PENDIENTE);
		}
		if (entregaDTO != null && Venta.RETIRO_INMEDIATO.equalsIgnoreCase(retiro)) {
			entregaDTO.setEstado("");
		}
		
		// Obtiene los items de la factura
		ventaDTO.getFacturaDTO().setItems(getItemsFactura());
		
		try {
			// Guardado de venta
			Venta ventaGuardada = ventaService.save(ventaDTO);
			ventaDTO.setId(ventaGuardada.getId());
			
			// Guardado de venta historico
			ventaHistoricoService.save(ventaGuardada);
						
			// Movimiento de caja producido por la venta
			MovimientoCajaDTO movimientoCajaDTO = new MovimientoCajaDTO();
			Double monto = new Double("0");
			// Si la venta tuvo una MODIFICACION de monto genero un movimiento de caja por la diferencia
			if (ventaDTO.getId() != null && !"".equals(ventaDTO.getId())) {
				Double nuevoMonto = ventaGuardada.getFactura().getMonto();
				Double viejoMonto = ventaVieja.getFacturaDTO().getMonto();
				if (nuevoMonto != viejoMonto) {
					monto = nuevoMonto - viejoMonto;
				}
			// Producido por una CREACION
			} else {
				monto = ventaDTO.getFacturaDTO().getMonto();
			}
			movimientoCajaDTO.setFecha(new Date());
			movimientoCajaDTO.setOrigen("VENTA");
			movimientoCajaDTO.setMonto(monto);
			movimientoCajaDTO.setVentaDTO(ventaDTO);
			movimientoCajaService.save(movimientoCajaDTO);
			
		} catch (CannotSaveException e) {
			setOk("false");
			setMensaje("No se ha podido almacenar la venta (o el movimiento de caja)");
			return SUCCESS_AGREGAR;
		}
		setOk("true");
		setMensaje("La venta se ha almacenado correctamente");
		return SUCCESS_AGREGAR;
	}
	
	/**
	 * Borrar un item de la factura
	 * @return
	 */
	public String borrarItem() {
		
		// TODO hacer validacion sobre ID
		
		try {
			itemFacturaService.delete(id);
		} catch (CannotDeleteException e) {
			setOk("false");
			setMensaje("El item no se ha podido eliminar");
			return SUCCESS_AGREGAR;
		}
		
		setOk("true");
		setMensaje("El item se elimino correctamente");
		return SUCCESS_AGREGAR;
	}
	
	/**
	 * Borra un venta
	 * @return
	 */
	public String borrar() {
		
		// TODO hacer validacion sobre ID
		
		try {
			ventaService.delete(id);
		} catch (CannotDeleteException e) {
			setOk("false");
			setMensaje("La venta no se ha podido eliminar");
			return SUCCESS_AGREGAR;
		}
		setOk("true");
		setMensaje("El venta se ha eliminado correctamente");
		return SUCCESS_AGREGAR;
	}

	/**
	 * Obtiene una lista de todos los ventas
	 * @return	la lista
	 */
	public String getVentas() {

		List<VentaDTO> ventas = ventaService.findAll("id", false);
		
		Gson gson = GSonHelper.getInstance();
		
		String ventasStr = gson.toJson(ventas);
		
		setLista(ventasStr);
		return SUCCESS_LISTADO;
	}
	
	/**
	 * Obtiene un venta
	 * @return el venta
	 */
	public String getVenta() {
		
		// TODO validacion sobre ID
		
		VentaDTO venta = null;
		try {
			venta = ventaService.findById(id);
		} catch (EntityNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		Gson gson = GSonHelper.getInstance();
		
		String ventaStr = gson.toJson(venta);
		setLista(ventaStr);
		return SUCCESS_JSON;
	}

	
	/**
	 * Busca ventas
	 */
	public String buscar() {
		
		// TODO validacion sobre DTO
		
		SearchDTO searchDTO = new SearchDTO();
		searchDTO.addBeetwenCriteria("fecha", fechaInicio, fechaFin);
		
		List<VentaDTO> ventas = ventaService.search(searchDTO);
		
		Gson gson = GSonHelper.getInstance();
		String ventasStr = gson.toJson(ventas);
		
		setLista(ventasStr);
		return SUCCESS_LISTADO;
	}
	
	
	
	/**
	 * Obtiene los items de la factura. Lo arma a partir del string enviado por POST
	 * Formato:  ID|ID_PRODUCTO|CANTIDAD|PRECIO|TOTAL|DESCRIPCION;ID|ID_PRODUCTO|CANTIDAD|PRECIO|TOTAL|DESCRIPCION;ID|ID_PRODUCTO|CANTIDAD|PRECIO|TOTAL|DESCRIPCION; ....
	 * 
	 * @return set de itemsFacturaDTO
	 */
	private Set<ItemFacturaDTO> getItemsFactura() {
		
		Set<ItemFacturaDTO> set = new HashSet<ItemFacturaDTO>();
		
		String[] itemsArr = items.split(";");
		for (int i = 0; i < itemsArr.length; i++) {
			
			String[] item = itemsArr[i].split("\\|");
			
			String id = item[0];
			String idProducto = item[1];
			
			Integer cantidad = null;
			if (item[2] != null) {
				cantidad = Integer.parseInt(item[2]);
			}
			Double precio = null; 
			if (item[3] != null) {
				precio = Double.parseDouble(item[3]);
			}
			
			Double total = null; 
			if (item[4] != null) {
				total = Double.parseDouble(item[4]);
			}
			
			String descripcion = item[5];
			
			ItemFacturaDTO itemFacturaDTO = new ItemFacturaDTO();
			itemFacturaDTO.setId(id);
			
			ProductoDTO productoDTO = productoService.findById(idProducto);
			itemFacturaDTO.setProducto(productoDTO);
			
			itemFacturaDTO.setIdProducto(idProducto);
			itemFacturaDTO.setCantidad(cantidad);
			itemFacturaDTO.setPrecio(precio);
			itemFacturaDTO.setDescripcion(descripcion);
			itemFacturaDTO.setTotal(total);
			
			set.add(itemFacturaDTO);
		}
		return set;
	}
	
	/**
	 * Obtiene todos los criterios de cobro
	 * @return
	 */
	public String getCriteriosDeCobro() {
		
		List<PersistentObject> criterios = criterioDeCobroService.findAll();
		
		Gson gson = GSonHelper.getInstance();
		String criteriosStr = gson.toJson(criterios);
		setLista(criteriosStr);
		return SUCCESS_LISTADO;
	}
	
	
	/**
	 * Getters & Setters
	 */
	public VentaDTO getVentaDTO() {
		return ventaDTO;
	}

	public String getItems() {
		return items;
	}

	public void setItems(String items) {
		this.items = items;
	}

	public Date getFechaInicio() {
		return fechaInicio;
	}

	public void setFechaInicio(Date fechaInicio) {
		this.fechaInicio = fechaInicio;
	}

	public Date getFechaFin() {
		return fechaFin;
	}

	public void setFechaFin(Date fechaFin) {
		this.fechaFin = fechaFin;
	}

	public void setVentaDTO(VentaDTO ventaDTO) {
		this.ventaDTO = ventaDTO;
	}

	public ProductoService getProductoService() {
		return productoService;
	}

	public void setProductoService(ProductoService productoService) {
		this.productoService = productoService;
	}

	public VentaService getVentaService() {
		return ventaService;
	}

	public void setVentaService(VentaService ventaService) {
		this.ventaService = ventaService;
	}

	public ItemFacturaService getItemFacturaService() {
		return itemFacturaService;
	}

	public void setItemFacturaService(ItemFacturaService itemFacturaService) {
		this.itemFacturaService = itemFacturaService;
	}

	public MovimientoCajaService getMovimientoCajaService() {
		return movimientoCajaService;
	}

	public void setMovimientoCajaService(MovimientoCajaService movimientoCajaService) {
		this.movimientoCajaService = movimientoCajaService;
	}

	public VentaHistoricoService getVentaHistoricoService() {
		return ventaHistoricoService;
	}

	public void setVentaHistoricoService(VentaHistoricoService ventaHistoricoService) {
		this.ventaHistoricoService = ventaHistoricoService;
	}

	public CriterioDeCobroService getCriterioDeCobroService() {
		return criterioDeCobroService;
	}

	public void setCriterioDeCobroService(
			CriterioDeCobroService criterioDeCobroService) {
		this.criterioDeCobroService = criterioDeCobroService;
	}

	public List<FormaDeCobroDTO> getFormasDeCobro() {
		return formasDeCobro;
	}

	public void setFormasDeCobro(List<FormaDeCobroDTO> formasDeCobro) {
		this.formasDeCobro = formasDeCobro;
	}

}
